package morenoapp.components.input;

import java.awt.Color;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import morenoapp.MorenoApp;
import morenoapp.model.data.MorenoAnswerFace;
import morenoapp.model.data.MorenoAnswerItemFace;
import morenoapp.model.data.MorenoModelFace;
import morenoapp.model.data.MorenoQuestionFace;
import morenoapp.model.data.MorenoQuestionItemFace;
import morenoapp.model.data.MorenoRespondentFace;
import morenoapp.model.listeners.MorenoModelAdapter;
import morenoapp.util.I18N;
import morenoapp.util.MorenoUtil;
import org.mb.listeners.IListenerManager;
import org.mb.listeners.RefListenerManager;

/**
 * 
 * @author dmitry.mamonov
 */
public class InputAnswersTableModel implements TableModel {
    private static final String[] COLUM_NAMES = new String[]{
        I18N.get("label.position"),
        I18N.get("label.respondent_choose")};
    private IListenerManager<TableModelListener> listenerManager = new RefListenerManager<TableModelListener>(TableModelListener.class);
    private TableModelListener listenersProxy = listenerManager.getProxy();
    private final int shortViewRowCount = 5;
    private boolean shortView = true;
    private MorenoRespondentFace respondent = null;
    private TableSection[] sections = null;

    public InputAnswersTableModel() {
        getModel().addModelListener(new MorenoModelAdapter() {
            @Override
            public void onQuestionsListChanged() {
                reset();
            }

            @Override
            public void onRespondentsListChanged() {
                reset();
            }

            @Override
            public void onWholeModelChanged() {
                reset();
            }
        });
    }

    public boolean isShortView() {
        return shortView;
    }

    public void setShortView(boolean shortView) {
        this.shortView = shortView;
        reset();
    }

    public String getStatusMessage() {
        if (respondent == null) {
            return I18N.get("label.select_respondent_in_list");
        } else if (respondent.isPassedPoll()) {
            return I18N.get("label.respondent_passed_poll");
        } else {
            return null;
        }
    }

    void setRespondent(MorenoRespondentFace aRespondent) {
        this.respondent = aRespondent;
        reset();
    }

    /* -- private methods -- */
    private MorenoModelFace getModel() {
        return MorenoApp.getApplicationData().getMorenoModelProxy();
    }

    private int getMaxAnswerItemNumber(MorenoAnswerFace answer) {
        int maxNumber = 0;
        for (MorenoAnswerItemFace answerItem : answer.getAnswerItems()) {
            int currentQuestionItemNumber = answerItem.getQuestionItem().getItemNumber();
            if (currentQuestionItemNumber > maxNumber && answerItem.getTarget() != null) {
                maxNumber = currentQuestionItemNumber;
            }
        }
        System.out.println("max number: " + answer.getQuestion().toPlainString() + ": " + maxNumber); //NOI18N
        return maxNumber;
    }

    public void reset() {
        sections = null;
        listenersProxy.tableChanged(new TableModelEvent(this));
    }

    private TableSection[] getSections() {
        if (sections == null) {
            if (respondent == null) {
                sections = new TableSection[0];
            } else if (respondent.isPassedPoll()) {
                sections = new TableSection[0];
            } else {
                sections = new TableSection[getModel().getQuestionsList().size()];
                int index = 0;
                int endRow = 0;
                for (MorenoQuestionFace question : getModel().getQuestionsList()) {
                    sections[index] = new TableSection(question, endRow);
                    endRow = sections[index].getEndRow() + 1;
                    index++;
                }
            }
        }
        return sections;
    }

    private TableSection getSectionByRow(int row) {
        int currentRow = row;
        for (TableSection section : getSections()) {
            if (currentRow < section.getRowCountWithHeader()) {
                return section;
            } else {
                currentRow -= section.getRowCountWithHeader();
            }
        }
        throw new IllegalArgumentException("No table section for row: " + row); //NOI18N
    }

    private MorenoAnswerItemFace getAnswerItemByRow(int rowIndex) {
        TableSection section = getSectionByRow(rowIndex);
        int relativeRow = section.getRelativeRow(rowIndex);
        if (relativeRow == 0) {
            throw new IllegalArgumentException("No answer item at row: " + rowIndex); //NOI18N
        }

        int rowNumber = relativeRow; //now row number starts from 1.
        MorenoQuestionFace question = section.getQuestion();
        MorenoQuestionItemFace questionItem = question.getQuestionItemByNumber(rowNumber);

        MorenoAnswerFace answer = respondent.getAnswerByQuestion(question);
        return answer.getAnswerItemByQuestionItem(questionItem);
    }

    /* -- implementation -- */
    @Override
    public int getRowCount() {
        int totalRowCount = 0;
        for (TableSection section : getSections()) {
            totalRowCount += section.getRowCountWithHeader();
        }
        return totalRowCount;
    }

    @Override
    public int getColumnCount() {
        return COLUM_NAMES.length;
    }

    @Override
    public String getColumnName(int columnIndex) {
        return COLUM_NAMES[columnIndex];
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return String.class;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        if (columnIndex != 1) {
            return false;
        } else {
            if (respondent == null) {
                return false;
            } else {
                TableSection section = getSectionByRow(rowIndex);
                return section.isRelativeRowEditable(section.getRelativeRow(rowIndex));
            }
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        TableSection section = getSectionByRow(rowIndex);
        int relativeRow = section.getRelativeRow(rowIndex);
        if (relativeRow == 0) {
            if (columnIndex == 0) {
                return I18N.get("label.question_colon");
            }
            if (columnIndex == 1) {
                MorenoQuestionFace q = section.getQuestion();
                return ("<html>" + //NOI18N
                        MorenoUtil.wrapTextWithColor(
                        q.getColor(),
                        q.getNumber() + ". " + //NOI18N
                        "[" + //NOI18N
                        q.getType().getLongTitle() +
                        "] " + //NOI18N
                        q.getContent())).replace(" ", "&nbsp;"); //NOI18N
            } else {
                return "";
            }
        } else {
            int rowNumber = relativeRow; //now row number starts from 1.
            MorenoQuestionFace question = section.getQuestion();
            MorenoQuestionItemFace questionItem = question.getQuestionItemByNumber(rowNumber);
            if (columnIndex == 0) {
                return "<html>" + MorenoUtil.wrapTextWithColor(question.getColor(), String.format("%2d.", questionItem.getItemNumber())); //NOI18N
            } else {

                if (respondent == null) {
                    if (columnIndex == 1) {
                        return new AnswerCellValue("<html>" + MorenoUtil.wrapTextWithColor(Color.lightGray, I18N.get("label.do_select_respondent_in_list_nbsp")), null);
                    } else {
                        return "";
                    }
                } else {
                    MorenoAnswerItemFace answerItem = getAnswerItemByRow(rowIndex);
                    if (columnIndex == 1) {
                        if (answerItem.getTarget() != null) {
                            return new AnswerCellValue("<html>" + answerItem.getTarget().toHtmlString(), answerItem); //NOI18N
                        } else {
                            return new AnswerCellValue("<html>" + MorenoUtil.wrapTextWithColor(Color.lightGray, I18N.get("label.specify_choose_nbsp")), answerItem);
                        }
                    } else {
                        return "";
                    }
                }
            }
        }
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        MorenoAnswerItemFace answerItem = getAnswerItemByRow(rowIndex);
        answerItem.setTarget((MorenoRespondentFace) aValue);
    }

    @Override
    public void addTableModelListener(TableModelListener l) {
        listenerManager.addListener(l);
    }

    @Override
    public void removeTableModelListener(TableModelListener l) {
        listenerManager.removeListener(l);
    }

    class TableSection {
        private MorenoQuestionFace question;
        private int startRow;
        private int countRows;

        public TableSection(MorenoQuestionFace question, int startRow) {
            this.question = question;
            this.startRow = startRow;

            countRows = isShortView() ? shortViewRowCount : getModel().getRespondentsList().size() - 1;
            if (respondent != null && !isShortView()) {
                MorenoAnswerFace answer = respondent.getAnswerByQuestion(question);
                int maxAnswerItemNumber = getMaxAnswerItemNumber(answer);
                if (maxAnswerItemNumber >= countRows - 1) {
                    countRows = maxAnswerItemNumber + 2;
                }
            }
            countRows = Math.min(countRows, getModel().getRespondentsList().size() - 1);
        }

        public int getStartRow() {
            return startRow;
        }

        public int getEndRow() {
            return startRow + countRows; //note: 1 row of type header omitted hence formula is correct
        }

        public int getRelativeRow(int row) {
            int relativeRow = row - getStartRow();
            if (relativeRow >= 0 && relativeRow < getRowCountWithHeader()) {
                return relativeRow;
            } else {
                throw new IllegalArgumentException("Row " + row + " is not related to " + this); //NOI18N
            }
        }

        public boolean isRelativeRowEditable(int relativeRow) {
            return relativeRow > 0;
        }

        public int getRowCountWithHeader() {
            return getRowCount() + 1;
        }

        public int getRowCount() {
            return countRows;
        }

        public MorenoQuestionFace getQuestion() {
            return question;
        }

        @Override
        public String toString() {
            return getClass().getSimpleName() + " [" + getStartRow() + ", " + getEndRow() + "]"; //NOI18N
        }
    }

    public class AnswerCellValue {
        protected String text;
        protected MorenoAnswerItemFace answerItem;

        public AnswerCellValue(String text, MorenoAnswerItemFace answerItem) {
            this.text = text;
            this.answerItem = answerItem;
        }

        public MorenoAnswerItemFace getAnswerItem() {
            return answerItem;
        }

        public String getText() {
            return text;
        }

        @Override
        public String toString() {
            return text;
        }
    }
}
